package com.xiongjie.SingleLimit.algorithm;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

/**
 * 滑动窗口限流算法的核心：
 * 时间窗口数组，数组的元素是原子数
 * 总体的的计数器
 * 循环窗口移动，计数器的个数加减
 */
public class SWLimiter implements Limiter, Runnable {

    private final long maxVisitPerSecond;
    private final AtomicInteger[] countPerBlock;    //窗口，每个记录有访问次数
    private final int block;    //窗口上限
    private volatile int index; //原子移动下标

    private AtomicInteger count;    //时间短内的总体访问次数上限


    public SWLimiter(int block, long maxVisitPerSecond) {
        this.block = block;
        this.maxVisitPerSecond = maxVisitPerSecond;

        countPerBlock = new AtomicInteger[block];
        for (int i = 0; i < block; i++) {
            countPerBlock[i] = new AtomicInteger();
        }
        count = new AtomicInteger(0);
    }

    @Override
    public boolean isOverLimit() {
        return currentQPS() > maxVisitPerSecond;
    }

    @Override
    public int currentQPS() {
        return count.get();
    }

    @Override
    public boolean visit() {
        countPerBlock[index].incrementAndGet();
        count.incrementAndGet();
        return isOverLimit();
    }

    @Override
    public void run() {
        System.out.println("是否超过限流："+isOverLimit() + "  " + currentQPS() + " index:" + index);
        index = (index + 1) % block;

        //这是循环滑动窗口，当全部窗口都被访问时，需要将原来的窗口清空
        int val = countPerBlock[index].getAndSet(0);
        count.addAndGet(-val);
    }

    public static void main(String[] args) {

        //设置滑动窗口，设置限流1秒钟内最多访问1000次，将1秒钟拆分为10个窗口，延迟0.1秒初始化，每隔0.1秒重置计数器
        SWLimiter slidingWindowRateLimiter = new SWLimiter(10, 1000);
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        scheduledExecutorService.scheduleAtFixedRate(slidingWindowRateLimiter, 100, 1000, TimeUnit.MILLISECONDS);

        //模拟0.1秒中发送一次请求
        IntStream.range(0, 2).forEach(i -> {
            new Thread(() -> {
                while (true) {
                    boolean res = slidingWindowRateLimiter.visit();
                    System.out.print(res + "\t");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        });
    }
}
